﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Fetches.Fetch.Dto;
using Fetches.Fetch.Factory;
using Fetches.Fetch.Factory.Persistence;
using Fetches.Fetch.PagePool.Consume;
using Fetches.Fetch.Utils;
using FetchesDeploy.Distributed.Factory.Mapping;
using FetchesDeploy.Distributed.UnitMonitor;
using Serilog;

namespace FetchesDeploy.Distributed.Factory
{
    /// <summary>
    ///     可伸缩采集工厂，根据加入调度的单位实例拆分任务分配Fetcher
    /// </summary>
    public class ScalableFetchFactory : AbstractFetchFactory
    {
        public static ScalableFetchFactory Instance { get; private set; }
        public FactoryConfig CurrentFactoryConfig { get; private set; }

        #region 调度用缓存信息

        /// <summary>
        /// workshop集合
        /// </summary>
        private readonly ConcurrentDictionary<long, UnitSchedules> _factoryUnits =
            new ConcurrentDictionary<long, UnitSchedules>();

        /// <summary>
        ///     fetcher到Workshop的映射，目前实际维护的是工作切片到Workshop的映射
        /// </summary>
        private readonly ConcurrentDictionary<long, UnitSchedules> _fetcher2Workshops =
            new ConcurrentDictionary<long, UnitSchedules>();
        private readonly ConcurrentDictionary<long, FetchTask> _taskDictionary =
            new ConcurrentDictionary<long, FetchTask>();

        private readonly FetcherType2Unit _fetcherType2Unit = new FetcherType2Unit();

        private readonly FetcherPool _fetcherPool = new FetcherPool();
        #endregion

        private readonly IRuntimeDataService RuntimeDataService;
        private IUnitMonitor[] _unitMonitors;
        #region ctor

        /// <summary>
        ///  虚拟工厂会从数据重新构建,控制中心和container谁先启动不重要
        /// </summary>
        /// <param name="runtimeDataService"></param>
        /// <param name="unitMonitors"></param>
        public ScalableFetchFactory(IRuntimeDataService runtimeDataService,params IUnitMonitor[] unitMonitors)
        {
            Instance = this;
            RuntimeDataService = runtimeDataService;
            _unitMonitors = unitMonitors;

            foreach (var workshop in RuntimeDataService.LoadAllUnitSchedules())
            {
                UnitOnline(workshop);
            }

            foreach (var fetchTask in RuntimeDataService.LoadUnfinishedFetchTask())
            {
                AddUnfinishedFetchTask(fetchTask);
            }

            foreach (var unitMonitor in unitMonitors)
            {
                unitMonitor.Subscribe(UnitOnline, UnitOffline);
            }
            
            //构建完成后开始定时调度
            Schedule();
            Log.Debug("virtual factory has been initialized");
        }

        #endregion

        #region workshop

        public void UnitOnline(UnitSchedules factoryUnit)
        {
            if (_factoryUnits.TryGetValue(factoryUnit.Id, out var existsWorkshop))
            {
                //foreach (var workshopFetchers in dispatchInfo)
                //{
                //    var workshopId = workshopFetchers.Key;
                //    var dispatchedFetchers = workshopFetchers.Value;
                //    foreach (var dispatchedFetcher in dispatchedFetchers)
                //    {
                //        RegisterFetcherDispatchInfo(workshopId, dispatchedFetcher.Value, dispatchedFetcher.Key);
                //    }

                //}
            }
            else
            {
                //factoryUnit注册成功后登记fetcher类型的支持信息
                if (_factoryUnits.TryAdd(factoryUnit.Id, factoryUnit))
                {
                    _fetcherType2Unit.RegisterUnit(factoryUnit);
                }
            }

        }

        /// <summary>
        /// </summary>
        /// <param name="factoryUnitId"></param>
        /// <returns></returns>
        public void UnitOffline(long factoryUnitId)
        {
            //车间下线
            if (_factoryUnits.TryRemove(factoryUnitId, out var factoryUnit))
            {
                _fetcherType2Unit.UnRegisterUnit(factoryUnit);
                //回收分配的任务切片到待分配区
                foreach (var dispatchedFetcher in factoryUnit.DispatchedFetchers)
                {
                    _fetcherPool.PutFetchersIntoRestZone(dispatchedFetcher.Value, dispatchedFetcher.Key);
                }

            }

        }

        #endregion

        #region 内部Task管理

        private void AddUnfinishedFetchTask(FetchTask task)
        {
            //登记任务分片
            _fetcherPool.RegisterUnfinishedFetchTask(task);
            _fetcherPool.PutFetchersIntoRestZone(task.FetcherIds, task.FetchOrder.FetcherTypeName);
            _taskDictionary.TryAdd(task.Id, task);
        }

        //登记Fetcher到Workshop的分配信息
        private void RegisterFetcherDispatchInfo(long workshopId, ISet<long> fetcherIds, string fetcherType)
        {
            if (_factoryUnits.TryGetValue(workshopId, out UnitSchedules workshop))
            {
                foreach (var fetcherId in fetcherIds)
                {
                    _fetcher2Workshops.TryAdd(fetcherId, workshop);
                    workshop.DispatchFetcher(fetcherId, fetcherType);
                    _fetcherPool.RemoveFetcherFromRestZone(fetcherId, fetcherType);
                }
            }
        }

        #endregion

        #region 外部调用Adaptor方法

        public void ChangeFactoryConfig(FactoryConfig workshopConfig)
        {
            RuntimeDataService.WriteFactoryConfig(workshopConfig);
        }

        public void Rebuild()
        {
            RuntimeDataService.Rebuild();
        }

        /// <summary>
        /// 接单
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public FetchReceipt TakeFetchOrder(FetchOrder order)
        {
            return ActuallyFetch(order);
        }

        /// <summary>
        /// 取消采集订单
        /// </summary>
        /// <param name="taskId"></param>
        public void CancelFetchTask(long taskId)
        {
            Log.Debug("task {task} is finished", taskId);
            if (_taskDictionary.TryRemove(taskId, out var task))
            {
                RuntimeDataService.UpdateFetchTaskStatus(taskId, FetchTaskStatus.Canceled);
                _fetcherPool.UnRegisterFetchTask(task);
                foreach (var fetcherId in task.FetcherIds)
                {
                    if (_fetcher2Workshops.TryGetValue(fetcherId, out UnitSchedules workshop))
                    {
                        workshop.DismissFetcher(fetcherId, task.FetchOrder.FetcherTypeName);
                    }
                }
            }

        }

        #endregion

        /// <summary>
        /// 目前的设计任务不是立即分配，而是定时调度的
        /// </summary>
        private void Schedule()
        {
            new Thread(() =>
            {
                while (true)
                {
                    //待分配Fetcher是以Fetcher类型分组的
                    foreach (var groupedRestingFetchers in _fetcherPool.GetGroupedRestingFetchTask())
                    {
                        var fetcherType = groupedRestingFetchers.Key;
                        var workshops = _fetcherType2Unit.SelectSupportedUnit(fetcherType);
                        if (workshops == null || !workshops.Any())
                        {
                            continue;
                        }
                        foreach (var restingFetcher in groupedRestingFetchers.Value.ToList())
                        {
                            //每次都取已分配任务最少的Workshop分配新任务
                            var workshop = workshops.OrderBy(w => w.DispatchedFetchers.Sum(f => f.Value.Count)).Take(1).First();
                            RegisterFetcherDispatchInfo(workshop.Id, new HashSet<long> { restingFetcher }, fetcherType);
                            DispatchFetcher(workshop, restingFetcher);

                        }

                    }
                    //目前设计30秒一次调度
                    Thread.Sleep(30 * 1000);
                }
            }).Start();
        }

        /// <summary>
        /// 这里是http还是etcd是个问题
        /// </summary>
        /// <param name="workshop"></param>
        /// <param name="fetcherId"></param>
        private void DispatchFetcher(UnitSchedules workshop, long fetcherId)
        {
            HttpClient.Post($"{workshop.Endpoint}/fetcher/{fetcherId}", "", out var isSuccess);
        }

        #region IFetchFactory

        /// <summary>
        /// 可伸缩采集工厂处理订单并不是立即调度的，排队等待按策略调度
        /// </summary>
        /// <param name="order"></param>
        protected override FetchReceipt ActuallyFetch(FetchOrder order)
        {
            var task = FetchOrder2Task(order);

            AddUnfinishedFetchTask(task);
            RuntimeDataService.RegisterOrUpdateFetchTask(task);

            var receipt = new FetchReceipt()
            {
                Id = task.Id,
                FetcherIds = task.FetcherIds
            };

            Log.Information("LocalFetchFactory start Fetch with order {@order},receipt {@receipt}", order, task);
            return receipt;
        }

        public override void SpeedUpFetch(FetchReceipt receipt, int fetchers2Hire)
        {
            throw new NotImplementedException();
        }

        public override void SpeedDownFetch(FetchReceipt receipt, int fetchers2Dismiss)
        {
            throw new NotImplementedException();
        }

        public override void StopFetch(FetchReceipt receipt)
        {
            throw new NotImplementedException();
        }

        public override void ContinueFetch(FetchReceipt receipt)
        {
            throw new NotImplementedException();
        }

        public override void Watch(FetchReceipt receipt, Action<FetchTaskProgress> progressAction)
        {
            throw new NotImplementedException();
        }

        public override ProcessReceipt Process(ProcessOrder processOrder)
        {
            throw new NotImplementedException();
        }

        public override PagePoolConsumer Consume(FetchReceipt receipt)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}